package org.wzy.spring.framework.webmvc.servlet;

import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 模拟 SpringMVC ViewResolver 实现, 主要是实现: 将静态文件变为动态文件, 相应用户的参数
 * <p>
 * 我们定义:
 * 假设用户的界面中包含 ￥{} 这样的代码块, 则表明此处需要被动态赋值(类似于其它一些动态视图技术中的 ${} 代码块)
 *
 * @author 王政豫
 * @date 2018/5/22 14:44
 */
class ViewResolver {

    private final static Pattern REGEX_COMPILE = Pattern.compile("￥\\{(.+?)}", Pattern.CASE_INSENSITIVE);

    private String fileName;

    private File file;

    ViewResolver(String fileName, File file) {
        this.fileName = fileName;
        this.file = file;
    }

    private static Matcher matcher(String content) {
        return REGEX_COMPILE.matcher(content);
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    /**
     * 这一步是将用户返回的页面渲染出来, 在 Spring 中此方法只负责找到对应的视图文件并返回, 真正的渲染方法是 render
     *
     * @param modelAndView 包含将要被渲染的视图名称
     * @throws FileNotFoundException 未找到对应的视图文件
     * @author 王政豫
     * @date 2018/5/24 15:52
     */
    String resolveViewName(ModelAndView modelAndView) throws IOException {
        if (null == modelAndView || StringUtils.isEmpty(modelAndView.getViewName())) {
            throw new NullPointerException("view name can not be null!");
        }

        // 使用输出流将内容动态输出
        StringBuilder sb = new StringBuilder();
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(this.file, "r")) {
            String line;
            String content;
            while (null != (line = randomAccessFile.readLine())) {
                content = new String(line.getBytes("ISO-8859-1"), "UTF-8");
                Matcher matcher = matcher(content);
                while (matcher.find()) {
                    // 需要把 ￥{xxx} 中间的 xxx 取出来
                    // 在扫描的过程中可能有多组符合条件的数据, 因此需要用到组的概念
                    for (int i = 0; i <= matcher.groupCount(); i++) {
                        // 注意, 如果正则匹配成功, 则 matcher.groupCount() 会返回 2 个结果, 第一个结果是原始值(如￥{userName}), 第二个才是正则转义之后的值(userName)
                        String paramName = matcher.group(i);
                        Object paramValue = modelAndView.getModel().get(paramName);
                        if (null == paramValue) {
                            continue;
                        }
                        content = content.replaceAll("￥\\{" + paramName + "\\}", paramValue.toString());
                        content = new String(content.getBytes("utf-8"), "ISO-8859-1");
                    }
                }
                sb.append(content);
            }
        }

        return sb.toString();
    }
}
